source('../settings/settings.R')
source('commonFunctions.R')
persons <- SELECTED_SUBJECTS
drive <- 4
inputFile <- str_interp('../data/processed/distancewise/TT1_Drive_${drive}_${distPrev}m_${distNext}m.csv', list(drive=drive, distPrev=30, distNext=30))
outputFile <- str_interp("../data/processed/analysis/TT1_Drive_${drive}_PP_${distPrev}m_${distNext}m.csv", list(drive=drive, distPrev=30, distNext=30))
all_Drive4 <- read.csv(inputFile)
all_Drive4$Subject <- as.factor(all_Drive4$Subject)
all_Drive4$logPerspiration <- log(all_Drive4$Perspiration)
# starting_points = c( 669 , 668 , 676 , 687 , 680 , 676 , 678 ,
# 693 , 722 , 723 , 677 , 679 , 711 , 707 ,
# 699 , 679 , 684 , 688 , 686 , 696 , 702 )
#
# ending_points = c( 741 , 786 , 749 , 782 , 736 , 756 , 768 ,
# 812 , 853 , 792 , 783 , 772 , 799 , 781 ,
# 777 , 763 , 795 , 791 , 832 , 755 , 758 )
peak_points = c( 67 , 86 , 73 , 73 , 73 , 64 , 73 ,
79 , 69 , 64 , 68 , 67 , 77 , 68 ,
82 , 67 , 72 , 72 , 71 , 68 , 64 )
# Driving time
driving_times = vector(mode="list", length = length(persons))
names(driving_times) <- persons
activity_names = vector(mode="list", length = length(persons))
names(activity_names) <- persons
acc_start_times = vector(mode="list", length = length(persons))
names(acc_start_times) <- persons
acc_end_times = vector(mode="list", length = length(persons))
names(acc_end_times) <- persons
stressor_start_times = vector(mode="list", length = length(persons))
names(stressor_start_times) <- persons
stressor_end_times = vector(mode="list", length = length(persons))
names(stressor_end_times) <- persons
complete_times = vector(mode="list", length = length(persons))
names(complete_times) <- persons
data_baseline = vector(mode="list", length=length(persons))
pp_baseline = vector(mode="list", length=length(persons))
names(data_baseline) <- persons
names(pp_baseline) <- persons
# Number of peaks
PREV_DISTANCE = 600 # 300
TRACKING_DISTANCE = 100
DRIVE_MODE = 4
getActivityName <- function(x, fullname=F) {
if(x == 1) return(ifelse(fullname, "Normal", "NO"))
if(x == 2) return(ifelse(fullname, "Cognitive", "C"))
if(x == 3) return(ifelse(fullname, "Motoric", "M"))
}
for (p in persons) {
pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
pAcc <- pData[pData$Failure>0.5,] # Failure = 1
acc_start_times[[p]] <- min(pAcc$Distance)
acc_end_times[[p]] <- max(pAcc$Distance)
activity_names[[p]] <- getActivityName(pData[pData$Time==60,]$Activity, fullname = T)
pStressor = pData[pData$Activity>1.5,] # Stressor = 2, 3
if (nrow(pStressor) > 0) {
stressor_start_times[[p]] <- min(pStressor$Distance)
stressor_end_times[[p]] <- max(pStressor$Distance)
} else {
stressor_start_times[[p]] <- NULL
stressor_end_times[[p]] <- NULL
}
}
DELAY_DISTANCE <- 50
idx <- 1
plt_AllAcc <- vector(mode="list", length=length(persons))
names(plt_AllAcc) <- persons
COLOR_ACC = "#02A3C8"
COLOR_PP = "#F28E8E"
COLOR_BRAKE = "#888888"
y1 <- list(
tickfont = list(color = COLOR_ACC),
title="% or °",
range=c(0, 100)
)
y2 <- list(
tickfont = list(color = COLOR_PP),
overlaying = "y",
side = "right",
title = "Perspiration [ln °C²]",
showgrid = FALSE,
range=c(min(all_Drive4$ppLogNormalized), max(all_Drive4$ppLogNormalized))
)
for (p in persons) {
pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
# Baseline
data_baseline[[p]] <- read.csv(str_interp("../data/processed/drives/T0${person}/T0${person}_Drive_1.csv", list(person=p)))
# Compute the mean
p_pp_nr <- data_baseline[[p]]$Perspiration
p_pp_nr <- p_pp_nr[!is.na(p_pp_nr)]
pp_baseline[[p]] <- log(mean(p_pp_nr))
# Incident
driving_times[[p]] <- max(pData$Distance)
incident_starting_time <- acc_start_times[[p]] # starting_points[idx]
incident_ending_time <- acc_end_times[[p]]
complete_times[[p]] <- ifelse(incident_starting_time + TRACKING_DISTANCE > driving_times[[p]], driving_times[[p]], incident_starting_time + TRACKING_DISTANCE)
from_time <- ifelse(incident_starting_time - PREV_DISTANCE >= 0, incident_starting_time - PREV_DISTANCE, 0)
to_time <- complete_times[[p]]
# print(paste("From", from_time))
# print(paste("Incident", incident_starting_time))
# print(paste("To", to_time))
pDataBefore <- pData[pData$Distance < incident_starting_time & pData$Distance >= from_time,]
pDataAfter <- pData[pData$Distance >= incident_starting_time + DELAY_DISTANCE & pData$Distance <= to_time,]
# print(nrow(pDataBefore))
# print(nrow(pDataAfter))
ppMeanBefore <- mean(pDataBefore$ppLogNormalized)
ppMeanAfter <- mean(pDataAfter$ppLogNormalized)
# dir.create(file.path('../figures/drive/', paste0('Drive_', DRIVE_MODE)), showWarnings = FALSE)
fname <- str_interp('../plots/drive/Drive_${drive}/P${person}.png', list(drive=DRIVE_MODE, person=p))
pData <- pData[pData$Distance >= from_time,]
plot_Acc <- plot_ly(pData, x = ~Distance, height=400, width=900) %>%
add_trace(name="Acceleration", y = ~Acceleration, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_ACC)) %>%
add_trace(name="Brake", y = ~Braking, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_BRAKE)) %>%
add_trace(name="Perspiration", y = ~ppLogNormalized, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_PP), yaxis = "y2") %>%
add_segments(x = min(pData$Distance), xend = max(pData$Distance), y = ppMeanBefore, yend = ppMeanBefore,
yaxis = "y2", name="Mean of Perspiration before the incident",
line=list(color=COLOR_PP, dash = 'dot')) %>%
add_segments(x = min(pData$Distance), xend = max(pData$Distance), y = ppMeanAfter, yend = ppMeanAfter,
yaxis = "y2", name="Mean of Perspiration after the incident",
line=list(color="darkred", dash = 'dot')) %>%
# add_segments(x = min(pData$Distance) - 0.1, xend = max(pData$Distance), y = pp_baseline[[p]], yend = pp_baseline[[p]],
# yaxis = "y2", name="Baseline PP (from Drive 1)",
# line=list(color="blue", dash = 'dot')) %>%
layout(
title=paste0("Stressor=", activity_names[[p]], ""),
xaxis=list(title="Distance [m]", range=c(0)),
yaxis=y1,
yaxis2=y2,
margin = list(l = 50, r = 50, b = 50, t = 50, pad = 4),
shapes = list(
# Holistic period
list(type = "rect", fillcolor = "red",
line = list(color = "red"), opacity = 0.3,
x0 = incident_starting_time, x1 = incident_ending_time, xref = "x",
y0 = 0, y1 = 100, yref = "y"),
# Stressor period
list(type = "rect", fillcolor = "yellow",
line = list(color = "yellow"), opacity = 0.1,
x0 = stressor_start_times[[p]], x1 = incident_starting_time, xref = "x",
y0 = 0, y1 = 100, yref = "y"),
list(type = "rect", fillcolor = "yellow",
line = list(color = "yellow"), opacity = 0.1,
x0 = incident_ending_time, x1 = stressor_end_times[[p]], xref = "x",
y0 = 0, y1 = 100, yref = "y")
),
legend = list(x = 0.1, y = 1, bgcolor = "rgba(0,0,0,0)", title="Metric"),
autosize = F
)
orca(plot_Acc, fname)
idx <- idx + 1
plt_AllAcc[[p]] <- plot_Acc
}
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
-
\
|
/
-
\
|
/
-
\
|
/
-
\
|
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
-
\
|
/
-
\
|
/
-
\
|
/
-
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
\
-
\
|
/
-
\
|
/
-
\
|
/
-
htmltools::tagList(plt_AllAcc)
# for (p in persons) {
# # Save image
# orca(plt_AllAcc[[p]], file = paste0("../plots/drive/Drive_4/T0", p, ".png"), scale = 2)
# }
idx <- 1
behavioralColumns <- c("Subject",
"Brake_u",
"Brake_std",
"PP_before",
"PP_u",
"PP_std",
"PP_dev")
behavioralMatrix <- matrix(nrow=length(persons), ncol = length(behavioralColumns))
# Careful about Subject 09
# selected_persons <- persons[persons != "09"]
for (p in persons) {
pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
incident_starting_time <- acc_start_times[[p]] # starting_points[idx]
incident_ending_time <- acc_end_times[[p]]
from_time <- ifelse(incident_starting_time - PREV_DISTANCE >= 0, incident_starting_time - PREV_DISTANCE, 0)
to_time <- complete_times[[p]]
dfBefore <- pData[pData$Distance < incident_starting_time & pData$Distance >= from_time,]
dfAfter <- pData[pData$Distance >= incident_starting_time + DELAY_DISTANCE & pData$Distance <= to_time,]
# diffSpeed <- mean(dfAfter$Speed) - mean(dfBefore$Speed)
brakeMean <- mean(dfAfter$Braking)
brakeStd <- mean(dfAfter$Braking)
ppMean <- mean(dfAfter$ppLogNormalized)
ppBefore <- mean(dfBefore$ppLogNormalized)
ppStd <- sd(dfAfter$ppLogNormalized)
mid_avg <- (pp_baseline[[p]] + mean(dfBefore$ppLogNormalized)) / 2
diffPP <- mean(dfAfter$ppLogNormalized) - mean(dfBefore$ppLogNormalized)
behavioralMatrix[idx, ] <- c(p,
round(brakeMean, digits=5),
round(brakeStd, digits=5),
round(ppBefore, digits=5),
round(ppMean, digits = 5),
round(ppStd, digits=5),
round(diffPP, digits=5))
idx <- idx + 1
}
# behavioralMatrix
behavioralDf <- as.data.frame(behavioralMatrix, stringsAsFactors=FALSE)
names(behavioralDf) <- behavioralColumns
behavioralDf
NA
clusteringDf <- behavioralDf
clusteringDf$Subject <- NULL
# clusteringDf$PP_dev_norm <- as.numeric(clusteringDf$PP_dev) / as.numeric(clusteringDf$PP_u)
# clusteringDf$PP_std_norm <- as.numeric(clusteringDf$PP_std) / as.numeric(clusteringDf$PP_u)
clusteringDf$Brake_u <- NULL
clusteringDf$Brake_std <- NULL
clusteringDf$PP_before <- NULL
clusteringDf$PP_u <- NULL
clusteringDf$PP_std <- NULL
# clusteringDf$PP_dev <- NULL
rownames(clusteringDf) <- paste0("#", persons)
for (col in names(clusteringDf)) {
clusteringDf[,col] <- as.numeric(as.character(clusteringDf[, col]))
# clusteringDf[,col] <- scale(clusteringDf[,col])
}
clusteringDf
dfActivity <- all_Drive4[all_Drive4$Time==60,] %>% select(c("Subject", "Activity"))
dfActivity$ActivityName <- sapply(dfActivity$Activity, getActivityName)
dfActivity$Subject <- as.factor(dfActivity$Subject)
rownames(dfActivity) <- NULL
dfActivity %>% select(c("Subject", "ActivityName"))
library(dendextend)
NUMBER_OF_CLUSTERS = 3
color_darkpink = "#e75480"
CLUSTER_BRANCH_COLORS <- c("blue", "red", color_darkpink)[1:NUMBER_OF_CLUSTERS]
CLUSTER_LABEL_COLORS <- c("blue", "red", color_darkpink)[1:NUMBER_OF_CLUSTERS]
behavioralMatrixClustering <- as.matrix(clusteringDf)
rownames(behavioralMatrixClustering) <- paste0(dfActivity$ActivityName, " - #", persons)
distMatrix <- dist(behavioralMatrixClustering, method="manhattan")
hresults <- distMatrix %>% hclust
hc <- hresults %>%
as.dendrogram %>%
set("nodes_cex", NUMBER_OF_CLUSTERS) %>%
set("labels_col", value = CLUSTER_LABEL_COLORS, k=NUMBER_OF_CLUSTERS) %>%
# set("leaves_pch", 19) %>%
# set("leaves_col", value = c("gray"), k=NUMBER_OF_CLUSTERS) %>%
set("branches_k_color", value=CLUSTER_BRANCH_COLORS, k=NUMBER_OF_CLUSTERS)
plot(hc)
legend("topright",
title="Drive=Failure \nChange of Arousal",
legend = c("Exceptional Increase" , "Noticable Increase" , "No-change or Decrease"),
col = c("red", "pink" , "blue"),
pch = c(20,20,20), bty = "n", pt.cex = 1.5, cex = 0.8 ,
text.col = "black", horiz = FALSE, inset = c(0.0, 0.1))

NUMBER_OF_CLUSTERS = 2
color_darkpink = "#e75480"
CLUSTER_BRANCH_COLORS <- c("blue", "red", color_darkpink)[1:NUMBER_OF_CLUSTERS]
CLUSTER_LABEL_COLORS <- c("blue", "red", color_darkpink)[1:NUMBER_OF_CLUSTERS]
behavioralMatrixClustering <- as.matrix(clusteringDf)
rownames(behavioralMatrixClustering) <- paste0(dfActivity$ActivityName, " - #", persons)
distMatrix <- dist(behavioralMatrixClustering, method="manhattan", diag = T)
hresults <- distMatrix %>% hclust(method="average")
hc <- hresults %>%
as.dendrogram %>%
set("nodes_cex", NUMBER_OF_CLUSTERS) %>%
set("labels_col", value = CLUSTER_LABEL_COLORS, k=NUMBER_OF_CLUSTERS) %>%
# set("leaves_pch", 19) %>%
# set("leaves_col", value = c("gray"), k=NUMBER_OF_CLUSTERS) %>%
set("branches_k_color", value=CLUSTER_BRANCH_COLORS, k=NUMBER_OF_CLUSTERS)
plot(hc)
legend("topright",
title="Drive=Failure \nChange of Arousal",
legend = c("Accelerophobia" , "Normal"),
col = c("red", "blue"),
pch = c(20,20,20), bty = "n", pt.cex = 1.5, cex = 0.8 ,
text.col = "black", horiz = FALSE, inset = c(0.0, 0.1))

# Store clustering data
dfx <- clusteringDf
dfx <- cbind(persons, as.numeric(behavioralDf$PP_before), as.numeric(behavioralDf$PP_u), dfx, dfActivity$ActivityName)
names(dfx) <- c("Subject", "PP_Prior", "PP_After", "PP_Dev", "Activity")
# Normalize
dfx <- dfx %>% mutate(PP_Dev_Normalized = ifelse(PP_Prior > 0, PP_After, PP_After - PP_Prior))
write.csv(dfx, outputFile, row.names = F)
library(cluster)
fit <- kmeans(clusteringDf, 2)
clusplot(clusteringDf, fit$cluster, color=TRUE, shade=TRUE,
labels=2, lines=0)

silhouette_score <- function(k){
km <- kmeans(clusteringDf, centers = k, nstart=25)
ss <- silhouette(km$cluster, dist(clusteringDf))
mean(ss[, 3])
}
k <- 2:10
avg_sil <- sapply(k, silhouette_score)
plot(k, type='b', avg_sil, xlab='Number of clusters', ylab='Average Silhouette Scores', frame=FALSE)

Linear Model
sampledData <- getSampleSegmentedData(NA, all_Drive4, window=2)
linearModelOnline <- lmer(ppNext ~
(1 | Subject)
+ Speed_u
+ Speed_std
+ Acc_u
+ Acc_std
+ Brake_u
+ Brake_std
+ Steering_u
+ Steering_std,
data=sampledData, REML = T)
# lmer(ppLogNormalized ~ (1 | Subject) + Speed_u + Speed_std + Acc_u + Acc_std + Brake_u + Brake_std + Steering_u + Steering_std + HR + BR, data = pData, REML = T)
# anova(model)
summary(linearModelOnline)
plot(linearModelOnline)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnNvdXJjZSgnLi4vc2V0dGluZ3Mvc2V0dGluZ3MuUicpCnNvdXJjZSgnY29tbW9uRnVuY3Rpb25zLlInKQpgYGAKCmBgYHtyfQpwZXJzb25zIDwtIFNFTEVDVEVEX1NVQkpFQ1RTCmRyaXZlIDwtIDQKCmlucHV0RmlsZSA8LSBzdHJfaW50ZXJwKCcuLi9kYXRhL3Byb2Nlc3NlZC9kaXN0YW5jZXdpc2UvVFQxX0RyaXZlXyR7ZHJpdmV9XyR7ZGlzdFByZXZ9bV8ke2Rpc3ROZXh0fW0uY3N2JywgbGlzdChkcml2ZT1kcml2ZSwgZGlzdFByZXY9MzAsIGRpc3ROZXh0PTMwKSkKb3V0cHV0RmlsZSA8LSBzdHJfaW50ZXJwKCIuLi9kYXRhL3Byb2Nlc3NlZC9hbmFseXNpcy9UVDFfRHJpdmVfJHtkcml2ZX1fUFBfJHtkaXN0UHJldn1tXyR7ZGlzdE5leHR9bS5jc3YiLCBsaXN0KGRyaXZlPWRyaXZlLCBkaXN0UHJldj0zMCwgZGlzdE5leHQ9MzApKQoKYWxsX0RyaXZlNCA8LSByZWFkLmNzdihpbnB1dEZpbGUpCmFsbF9Ecml2ZTQkU3ViamVjdCA8LSBhcy5mYWN0b3IoYWxsX0RyaXZlNCRTdWJqZWN0KQphbGxfRHJpdmU0JGxvZ1BlcnNwaXJhdGlvbiA8LSBsb2coYWxsX0RyaXZlNCRQZXJzcGlyYXRpb24pCgojIHN0YXJ0aW5nX3BvaW50cyA9IGMoIDY2OSAsIDY2OCAsIDY3NiAsIDY4NyAsIDY4MCAsICA2NzYgLCAgNjc4ICwKIyAgICAgICAgICAgICAgICAgICAgICA2OTMgLCA3MjIgLCA3MjMgLCA2NzcgLCA2NzkgLCAgNzExICwgIDcwNyAsICAKIyAgICAgICAgICAgICAgICAgICAgICA2OTkgLCA2NzkgLCA2ODQgLCA2ODggLCA2ODYgLCAgNjk2ICwgIDcwMiApCiMgCiMgZW5kaW5nX3BvaW50cyAgID0gYyggNzQxICwgNzg2ICwgNzQ5ICwgNzgyICwgNzM2ICwgIDc1NiAsICA3NjggLCAgCiMgICAgICAgICAgICAgICAgICAgICAgODEyICwgODUzICwgNzkyICwgNzgzICwgNzcyICwgIDc5OSAsICA3ODEgLCAgCiMgICAgICAgICAgICAgICAgICAgICAgNzc3ICwgNzYzICwgNzk1ICwgNzkxICwgODMyICwgIDc1NSAsICA3NTggKQoKcGVha19wb2ludHMgICAgID0gYyggNjcgLCAgODYgLCAgNzMgLCAgNzMgLCAgNzMgLCAgNjQgLCAgNzMgLCAgCiAgICAgICAgICAgICAgICAgICAgIDc5ICwgIDY5ICwgIDY0ICwgIDY4ICwgIDY3ICwgIDc3ICwgIDY4ICwgIAogICAgICAgICAgICAgICAgICAgICA4MiAsICA2NyAsICA3MiAsICA3MiAsICA3MSAsICA2OCAsICA2NCApCgojIERyaXZpbmcgdGltZQpkcml2aW5nX3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKGRyaXZpbmdfdGltZXMpIDwtIHBlcnNvbnMKCmFjdGl2aXR5X25hbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKGFjdGl2aXR5X25hbWVzKSA8LSBwZXJzb25zCgphY2Nfc3RhcnRfdGltZXMgPSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aCA9IGxlbmd0aChwZXJzb25zKSkKbmFtZXMoYWNjX3N0YXJ0X3RpbWVzKSA8LSBwZXJzb25zCmFjY19lbmRfdGltZXMgPSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aCA9IGxlbmd0aChwZXJzb25zKSkKbmFtZXMoYWNjX2VuZF90aW1lcykgPC0gcGVyc29ucwoKc3RyZXNzb3Jfc3RhcnRfdGltZXMgPSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aCA9IGxlbmd0aChwZXJzb25zKSkKbmFtZXMoc3RyZXNzb3Jfc3RhcnRfdGltZXMpIDwtIHBlcnNvbnMKc3RyZXNzb3JfZW5kX3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKHN0cmVzc29yX2VuZF90aW1lcykgPC0gcGVyc29ucwoKY29tcGxldGVfdGltZXMgPSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aCA9IGxlbmd0aChwZXJzb25zKSkKbmFtZXMoY29tcGxldGVfdGltZXMpIDwtIHBlcnNvbnMKCmRhdGFfYmFzZWxpbmUgPSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgocGVyc29ucykpCnBwX2Jhc2VsaW5lID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHBlcnNvbnMpKQoKbmFtZXMoZGF0YV9iYXNlbGluZSkgPC0gcGVyc29ucwpuYW1lcyhwcF9iYXNlbGluZSkgPC0gcGVyc29ucwoKIyBOdW1iZXIgb2YgcGVha3MKClBSRVZfRElTVEFOQ0UgPSA2MDAgIyAzMDAKVFJBQ0tJTkdfRElTVEFOQ0UgPSAxMDAKRFJJVkVfTU9ERSA9IDQKYGBgCgpgYGB7cn0KZ2V0QWN0aXZpdHlOYW1lIDwtIGZ1bmN0aW9uKHgsIGZ1bGxuYW1lPUYpIHsKICBpZih4ID09IDEpIHJldHVybihpZmVsc2UoZnVsbG5hbWUsICJOb3JtYWwiLCAiTk8iKSkKICBpZih4ID09IDIpIHJldHVybihpZmVsc2UoZnVsbG5hbWUsICJDb2duaXRpdmUiLCAiQyIpKQogIGlmKHggPT0gMykgcmV0dXJuKGlmZWxzZShmdWxsbmFtZSwgIk1vdG9yaWMiLCAiTSIpKQp9Cgpmb3IgKHAgaW4gcGVyc29ucykgewogIHBEYXRhIDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRTdWJqZWN0PT1hcy5pbnRlZ2VyKHApIHwgYWxsX0RyaXZlNCRTdWJqZWN0PT1wLF0KICBwQWNjIDwtIHBEYXRhW3BEYXRhJEZhaWx1cmU+MC41LF0gIyBGYWlsdXJlID0gMQogIGFjY19zdGFydF90aW1lc1tbcF1dIDwtIG1pbihwQWNjJERpc3RhbmNlKQogIGFjY19lbmRfdGltZXNbW3BdXSA8LSBtYXgocEFjYyREaXN0YW5jZSkKICAKICBhY3Rpdml0eV9uYW1lc1tbcF1dIDwtIGdldEFjdGl2aXR5TmFtZShwRGF0YVtwRGF0YSRUaW1lPT02MCxdJEFjdGl2aXR5LCBmdWxsbmFtZSA9IFQpCiAgCiAgcFN0cmVzc29yID0gcERhdGFbcERhdGEkQWN0aXZpdHk+MS41LF0gIyBTdHJlc3NvciA9IDIsIDMKICBpZiAobnJvdyhwU3RyZXNzb3IpID4gMCkgewogICAgc3RyZXNzb3Jfc3RhcnRfdGltZXNbW3BdXSA8LSBtaW4ocFN0cmVzc29yJERpc3RhbmNlKQogICAgc3RyZXNzb3JfZW5kX3RpbWVzW1twXV0gPC0gbWF4KHBTdHJlc3NvciREaXN0YW5jZSkKICB9IGVsc2UgewogICAgc3RyZXNzb3Jfc3RhcnRfdGltZXNbW3BdXSA8LSBOVUxMCiAgICBzdHJlc3Nvcl9lbmRfdGltZXNbW3BdXSA8LSBOVUxMCiAgfQp9CmBgYAoKYGBge3J9CkRFTEFZX0RJU1RBTkNFIDwtIDEwCgppZHggPC0gMQpwbHRfQWxsQWNjIDwtIHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChwZXJzb25zKSkgCm5hbWVzKHBsdF9BbGxBY2MpIDwtIHBlcnNvbnMKCkNPTE9SX0FDQyA9ICIjMDJBM0M4IgpDT0xPUl9QUCA9ICIjRjI4RThFIgpDT0xPUl9CUkFLRSA9ICIjODg4ODg4IgoKeTEgPC0gbGlzdCgKICB0aWNrZm9udCA9IGxpc3QoY29sb3IgPSBDT0xPUl9BQ0MpLAogIHRpdGxlPSIlIG9yIMKwIiwKICByYW5nZT1jKDAsIDEwMCkKKQp5MiA8LSBsaXN0KAogIHRpY2tmb250ID0gbGlzdChjb2xvciA9IENPTE9SX1BQKSwKICBvdmVybGF5aW5nID0gInkiLAogIHNpZGUgPSAicmlnaHQiLAogIHRpdGxlID0gIlBlcnNwaXJhdGlvbiBbbG4gwrBDwrJdIiwKICBzaG93Z3JpZCA9IEZBTFNFLAogIHJhbmdlPWMobWluKGFsbF9Ecml2ZTQkcHBMb2dOb3JtYWxpemVkKSwgbWF4KGFsbF9Ecml2ZTQkcHBMb2dOb3JtYWxpemVkKSkKKQogIApmb3IgKHAgaW4gcGVyc29ucykgewogIHBEYXRhIDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRTdWJqZWN0PT1hcy5pbnRlZ2VyKHApIHwgYWxsX0RyaXZlNCRTdWJqZWN0PT1wLF0KICAKICAjIEJhc2VsaW5lCiAgZGF0YV9iYXNlbGluZVtbcF1dIDwtIHJlYWQuY3N2KHN0cl9pbnRlcnAoIi4uL2RhdGEvcHJvY2Vzc2VkL2RyaXZlcy9UMCR7cGVyc29ufS9UMCR7cGVyc29ufV9Ecml2ZV8xLmNzdiIsIGxpc3QocGVyc29uPXApKSkKICAjIENvbXB1dGUgdGhlIG1lYW4KICBwX3BwX25yIDwtIGRhdGFfYmFzZWxpbmVbW3BdXSRQZXJzcGlyYXRpb24KICBwX3BwX25yIDwtIHBfcHBfbnJbIWlzLm5hKHBfcHBfbnIpXQogIHBwX2Jhc2VsaW5lW1twXV0gPC0gbG9nKG1lYW4ocF9wcF9ucikpCiAgCiAgIyBJbmNpZGVudAogIGRyaXZpbmdfdGltZXNbW3BdXSA8LSBtYXgocERhdGEkRGlzdGFuY2UpCiAgCiAgaW5jaWRlbnRfc3RhcnRpbmdfdGltZSA8LSBhY2Nfc3RhcnRfdGltZXNbW3BdXSAjIHN0YXJ0aW5nX3BvaW50c1tpZHhdCiAgaW5jaWRlbnRfZW5kaW5nX3RpbWUgPC0gYWNjX2VuZF90aW1lc1tbcF1dCiAgY29tcGxldGVfdGltZXNbW3BdXSA8LSBpZmVsc2UoaW5jaWRlbnRfc3RhcnRpbmdfdGltZSArIFRSQUNLSU5HX0RJU1RBTkNFID4gZHJpdmluZ190aW1lc1tbcF1dLCBkcml2aW5nX3RpbWVzW1twXV0sIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgKyBUUkFDS0lOR19ESVNUQU5DRSkKICAKICBmcm9tX3RpbWUgPC0gaWZlbHNlKGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgLSBQUkVWX0RJU1RBTkNFID49IDAsIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgLSBQUkVWX0RJU1RBTkNFLCAwKQogIHRvX3RpbWUgPC0gY29tcGxldGVfdGltZXNbW3BdXQogIAogICMgcHJpbnQocGFzdGUoIkZyb20iLCBmcm9tX3RpbWUpKQogICMgcHJpbnQocGFzdGUoIkluY2lkZW50IiwgaW5jaWRlbnRfc3RhcnRpbmdfdGltZSkpCiAgIyBwcmludChwYXN0ZSgiVG8iLCB0b190aW1lKSkKICAKICAgIAogIHBEYXRhQmVmb3JlIDwtIHBEYXRhW3BEYXRhJERpc3RhbmNlIDwgaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAmIHBEYXRhJERpc3RhbmNlID49IGZyb21fdGltZSxdCiAgcERhdGFBZnRlciA8LSBwRGF0YVtwRGF0YSREaXN0YW5jZSA+PSBpbmNpZGVudF9zdGFydGluZ190aW1lICsgREVMQVlfRElTVEFOQ0UgJiBwRGF0YSREaXN0YW5jZSA8PSB0b190aW1lLF0KICAKICAjIHByaW50KG5yb3cocERhdGFCZWZvcmUpKQogICMgcHJpbnQobnJvdyhwRGF0YUFmdGVyKSkKICAKICBwcE1lYW5CZWZvcmUgPC0gbWVhbihwRGF0YUJlZm9yZSRwcExvZ05vcm1hbGl6ZWQpCiAgcHBNZWFuQWZ0ZXIgPC0gbWVhbihwRGF0YUFmdGVyJHBwTG9nTm9ybWFsaXplZCkKICAKICAjIGRpci5jcmVhdGUoZmlsZS5wYXRoKCcuLi9maWd1cmVzL2RyaXZlLycsIHBhc3RlMCgnRHJpdmVfJywgRFJJVkVfTU9ERSkpLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKICBmbmFtZSA8LSBzdHJfaW50ZXJwKCcuLi9wbG90cy9kcml2ZS9Ecml2ZV8ke2RyaXZlfS9QJHtwZXJzb259LnBuZycsIGxpc3QoZHJpdmU9RFJJVkVfTU9ERSwgcGVyc29uPXApKSAKICAKICBwRGF0YSA8LSBwRGF0YVtwRGF0YSREaXN0YW5jZSA+PSBmcm9tX3RpbWUsXQogIHBsb3RfQWNjIDwtIHBsb3RfbHkocERhdGEsIHggPSB+RGlzdGFuY2UsIGhlaWdodD00MDAsIHdpZHRoPTkwMCkgJT4lCiAgICAgICAgICAgICAgYWRkX3RyYWNlKG5hbWU9IkFjY2VsZXJhdGlvbiIsIHkgPSB+QWNjZWxlcmF0aW9uLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywgbGluZT1saXN0KHdpZHRoPTEuNSwgY29sb3I9Q09MT1JfQUNDKSkgJT4lIAogICAgICAgICAgICAgIGFkZF90cmFjZShuYW1lPSJCcmFrZSIsIHkgPSB+QnJha2luZywgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsIGxpbmU9bGlzdCh3aWR0aD0xLjUsIGNvbG9yPUNPTE9SX0JSQUtFKSkgJT4lCiAgICAgICAgICAgICAgYWRkX3RyYWNlKG5hbWU9IlBlcnNwaXJhdGlvbiIsIHkgPSB+cHBMb2dOb3JtYWxpemVkLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywgbGluZT1saXN0KHdpZHRoPTEuNSwgY29sb3I9Q09MT1JfUFApLCB5YXhpcyA9ICJ5MiIpICU+JSAKICAgICAgICAgICAgICBhZGRfc2VnbWVudHMoeCA9IG1pbihwRGF0YSREaXN0YW5jZSksIHhlbmQgPSBtYXgocERhdGEkRGlzdGFuY2UpLCB5ID0gcHBNZWFuQmVmb3JlLCB5ZW5kID0gcHBNZWFuQmVmb3JlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSAieTIiLCBuYW1lPSJNZWFuIG9mIFBlcnNwaXJhdGlvbiBiZWZvcmUgdGhlIGluY2lkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZT1saXN0KGNvbG9yPUNPTE9SX1BQLCBkYXNoID0gJ2RvdCcpKSAlPiUKICAgICAgICAgICAgICBhZGRfc2VnbWVudHMoeCA9IG1pbihwRGF0YSREaXN0YW5jZSksIHhlbmQgPSBtYXgocERhdGEkRGlzdGFuY2UpLCB5ID0gcHBNZWFuQWZ0ZXIsIHllbmQgPSBwcE1lYW5BZnRlciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gInkyIiwgbmFtZT0iTWVhbiBvZiBQZXJzcGlyYXRpb24gYWZ0ZXIgdGhlIGluY2lkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZT1saXN0KGNvbG9yPSJkYXJrcmVkIiwgZGFzaCA9ICdkb3QnKSkgJT4lCiAgICAgICAgICAgICAgIyBhZGRfc2VnbWVudHMoeCA9IG1pbihwRGF0YSREaXN0YW5jZSkgLSAwLjEsIHhlbmQgPSBtYXgocERhdGEkRGlzdGFuY2UpLCB5ID0gcHBfYmFzZWxpbmVbW3BdXSwgeWVuZCA9IHBwX2Jhc2VsaW5lW1twXV0sIAogICAgICAgICAgICAgICMgICAgICAgICAgICAgIHlheGlzID0gInkyIiwgbmFtZT0iQmFzZWxpbmUgUFAgKGZyb20gRHJpdmUgMSkiLAogICAgICAgICAgICAgICMgICAgICAgICAgICAgIGxpbmU9bGlzdChjb2xvcj0iYmx1ZSIsIGRhc2ggPSAnZG90JykpICU+JQogICAgCiAgICAgICAgICAgICAgbGF5b3V0KAogICAgICAgICAgICAgICAgdGl0bGU9cGFzdGUwKCJTdHJlc3Nvcj0iLCBhY3Rpdml0eV9uYW1lc1tbcF1dLCAiIiksIAogICAgICAgICAgICAgICAgeGF4aXM9bGlzdCh0aXRsZT0iRGlzdGFuY2UgW21dIiwgcmFuZ2U9YygwKSksIAogICAgICAgICAgICAgICAgeWF4aXM9eTEsIAogICAgICAgICAgICAgICAgeWF4aXMyPXkyLCAKICAgICAgICAgICAgICAgIG1hcmdpbiA9IGxpc3QobCA9IDUwLCByID0gNTAsIGIgPSA1MCwgdCA9IDUwLCBwYWQgPSA0KSwKICAgICAgICAgICAgICAgIHNoYXBlcyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgICMgSG9saXN0aWMgcGVyaW9kCiAgICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9ICJyZWN0IiwgZmlsbGNvbG9yID0gInJlZCIsIAogICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIpLCBvcGFjaXR5ID0gMC4zLAogICAgICAgICAgICAgICAgICAgICAgeDAgPSBpbmNpZGVudF9zdGFydGluZ190aW1lLCB4MSA9IGluY2lkZW50X2VuZGluZ190aW1lLCB4cmVmID0gIngiLAogICAgICAgICAgICAgICAgICAgICAgeTAgPSAwLCB5MSA9IDEwMCwgeXJlZiA9ICJ5IiksCiAgICAgICAgICAgICAgICAgICMgU3RyZXNzb3IgcGVyaW9kCiAgICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9ICJyZWN0IiwgZmlsbGNvbG9yID0gInllbGxvdyIsIAogICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInllbGxvdyIpLCBvcGFjaXR5ID0gMC4xLAogICAgICAgICAgICAgICAgICAgICAgeDAgPSBzdHJlc3Nvcl9zdGFydF90aW1lc1tbcF1dLCB4MSA9IGluY2lkZW50X3N0YXJ0aW5nX3RpbWUsIHhyZWYgPSAieCIsCiAgICAgICAgICAgICAgICAgICAgICB5MCA9IDAsIHkxID0gMTAwLCB5cmVmID0gInkiKSwKICAgICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gInJlY3QiLCBmaWxsY29sb3IgPSAieWVsbG93IiwgCiAgICAgICAgICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAieWVsbG93IiksIG9wYWNpdHkgPSAwLjEsCiAgICAgICAgICAgICAgICAgICAgICB4MCA9IGluY2lkZW50X2VuZGluZ190aW1lLCB4MSA9IHN0cmVzc29yX2VuZF90aW1lc1tbcF1dLCB4cmVmID0gIngiLAogICAgICAgICAgICAgICAgICAgICAgeTAgPSAwLCB5MSA9IDEwMCwgeXJlZiA9ICJ5IikKICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICBsZWdlbmQgPSBsaXN0KHggPSAwLjEsIHkgPSAxLCBiZ2NvbG9yID0gInJnYmEoMCwwLDAsMCkiLCB0aXRsZT0iTWV0cmljIiksCiAgICAgICAgICAgICAgICBhdXRvc2l6ZSA9IEYKICAgICAgICAgICAgICApCiAgCiAgb3JjYShwbG90X0FjYywgZm5hbWUpCiAgaWR4IDwtIGlkeCArIDEKICBwbHRfQWxsQWNjW1twXV0gPC0gcGxvdF9BY2MKfQoKaHRtbHRvb2xzOjp0YWdMaXN0KHBsdF9BbGxBY2MpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CiMgZm9yIChwIGluIHBlcnNvbnMpIHsKIyAgICMgU2F2ZSBpbWFnZQojICAgb3JjYShwbHRfQWxsQWNjW1twXV0sIGZpbGUgPSBwYXN0ZTAoIi4uL3Bsb3RzL2RyaXZlL0RyaXZlXzQvVDAiLCBwLCAiLnBuZyIpLCBzY2FsZSA9IDIpCiMgfQpgYGAKCmBgYHtyfQppZHggPC0gMQpiZWhhdmlvcmFsQ29sdW1ucyA8LSBjKCJTdWJqZWN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIkJyYWtlX3UiLCAKICAgICAgICAgICAgICAgICAgICAgICAiQnJha2Vfc3RkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIlBQX2JlZm9yZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIlBQX3UiLCAgCiAgICAgICAgICAgICAgICAgICAgICAgIlBQX3N0ZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIlBQX2RldiIpCmJlaGF2aW9yYWxNYXRyaXggPC0gbWF0cml4KG5yb3c9bGVuZ3RoKHBlcnNvbnMpLCBuY29sID0gbGVuZ3RoKGJlaGF2aW9yYWxDb2x1bW5zKSkKCiMgQ2FyZWZ1bCBhYm91dCBTdWJqZWN0IDA5CiMgc2VsZWN0ZWRfcGVyc29ucyA8LSBwZXJzb25zW3BlcnNvbnMgIT0gIjA5Il0KCmZvciAocCBpbiBwZXJzb25zKSB7CiAgcERhdGEgPC0gYWxsX0RyaXZlNFthbGxfRHJpdmU0JFN1YmplY3Q9PWFzLmludGVnZXIocCkgfCBhbGxfRHJpdmU0JFN1YmplY3Q9PXAsXQogIAogIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgPC0gYWNjX3N0YXJ0X3RpbWVzW1twXV0gIyBzdGFydGluZ19wb2ludHNbaWR4XQogIGluY2lkZW50X2VuZGluZ190aW1lIDwtIGFjY19lbmRfdGltZXNbW3BdXQogIAogIGZyb21fdGltZSA8LSBpZmVsc2UoaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAtIFBSRVZfRElTVEFOQ0UgPj0gMCwgaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAtIFBSRVZfRElTVEFOQ0UsIDApCiAgdG9fdGltZSA8LSBjb21wbGV0ZV90aW1lc1tbcF1dCiAgICAKICBkZkJlZm9yZSA8LSBwRGF0YVtwRGF0YSREaXN0YW5jZSA8IGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgJiBwRGF0YSREaXN0YW5jZSA+PSBmcm9tX3RpbWUsXQogIGRmQWZ0ZXIgPC0gcERhdGFbcERhdGEkRGlzdGFuY2UgPj0gaW5jaWRlbnRfc3RhcnRpbmdfdGltZSArIERFTEFZX0RJU1RBTkNFICAmIHBEYXRhJERpc3RhbmNlIDw9IHRvX3RpbWUsXQogIAogICMgZGlmZlNwZWVkIDwtIG1lYW4oZGZBZnRlciRTcGVlZCkgLSBtZWFuKGRmQmVmb3JlJFNwZWVkKQogIGJyYWtlTWVhbiA8LSBtZWFuKGRmQWZ0ZXIkQnJha2luZykKICBicmFrZVN0ZCA8LSBtZWFuKGRmQWZ0ZXIkQnJha2luZykKICAKICBwcE1lYW4gPC0gbWVhbihkZkFmdGVyJHBwTG9nTm9ybWFsaXplZCkKICBwcEJlZm9yZSA8LSBtZWFuKGRmQmVmb3JlJHBwTG9nTm9ybWFsaXplZCkKICBwcFN0ZCA8LSBzZChkZkFmdGVyJHBwTG9nTm9ybWFsaXplZCkKICAKICBtaWRfYXZnIDwtIChwcF9iYXNlbGluZVtbcF1dICsgbWVhbihkZkJlZm9yZSRwcExvZ05vcm1hbGl6ZWQpKSAvIDIKICAgIAogIGRpZmZQUCA8LSBtZWFuKGRmQWZ0ZXIkcHBMb2dOb3JtYWxpemVkKSAtIG1lYW4oZGZCZWZvcmUkcHBMb2dOb3JtYWxpemVkKQogIAogIGJlaGF2aW9yYWxNYXRyaXhbaWR4LCBdIDwtIGMocCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChicmFrZU1lYW4sIGRpZ2l0cz01KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChicmFrZVN0ZCwgZGlnaXRzPTUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocHBCZWZvcmUsIGRpZ2l0cz01KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHBwTWVhbiwgZGlnaXRzID0gNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChwcFN0ZCwgZGlnaXRzPTUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoZGlmZlBQLCBkaWdpdHM9NSkpCiAgaWR4IDwtIGlkeCArIDEKfQoKIyBiZWhhdmlvcmFsTWF0cml4CgpiZWhhdmlvcmFsRGYgPC0gYXMuZGF0YS5mcmFtZShiZWhhdmlvcmFsTWF0cml4LCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQpuYW1lcyhiZWhhdmlvcmFsRGYpIDwtIGJlaGF2aW9yYWxDb2x1bW5zCgpiZWhhdmlvcmFsRGYKCmBgYAoKCmBgYHtyfQpjbHVzdGVyaW5nRGYgPC0gYmVoYXZpb3JhbERmCmNsdXN0ZXJpbmdEZiRTdWJqZWN0IDwtIE5VTEwKIyBjbHVzdGVyaW5nRGYkUFBfZGV2X25vcm0gPC0gYXMubnVtZXJpYyhjbHVzdGVyaW5nRGYkUFBfZGV2KSAvIGFzLm51bWVyaWMoY2x1c3RlcmluZ0RmJFBQX3UpCiMgY2x1c3RlcmluZ0RmJFBQX3N0ZF9ub3JtIDwtIGFzLm51bWVyaWMoY2x1c3RlcmluZ0RmJFBQX3N0ZCkgLyBhcy5udW1lcmljKGNsdXN0ZXJpbmdEZiRQUF91KQpjbHVzdGVyaW5nRGYkQnJha2VfdSA8LSBOVUxMCmNsdXN0ZXJpbmdEZiRCcmFrZV9zdGQgPC0gTlVMTApjbHVzdGVyaW5nRGYkUFBfYmVmb3JlIDwtIE5VTEwKY2x1c3RlcmluZ0RmJFBQX3UgPC0gTlVMTApjbHVzdGVyaW5nRGYkUFBfc3RkIDwtIE5VTEwKIyBjbHVzdGVyaW5nRGYkUFBfZGV2IDwtIE5VTEwKCnJvd25hbWVzKGNsdXN0ZXJpbmdEZikgPC0gcGFzdGUwKCIjIiwgcGVyc29ucykKCmZvciAoY29sIGluIG5hbWVzKGNsdXN0ZXJpbmdEZikpIHsKICBjbHVzdGVyaW5nRGZbLGNvbF0gPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoY2x1c3RlcmluZ0RmWywgY29sXSkpCiAgIyBjbHVzdGVyaW5nRGZbLGNvbF0gPC0gc2NhbGUoY2x1c3RlcmluZ0RmWyxjb2xdKQp9CmNsdXN0ZXJpbmdEZgpgYGAKCmBgYHtyfQpkZkFjdGl2aXR5IDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRUaW1lPT02MCxdICU+JSBzZWxlY3QoYygiU3ViamVjdCIsICJBY3Rpdml0eSIpKQpkZkFjdGl2aXR5JEFjdGl2aXR5TmFtZSA8LSBzYXBwbHkoZGZBY3Rpdml0eSRBY3Rpdml0eSwgZ2V0QWN0aXZpdHlOYW1lKQpkZkFjdGl2aXR5JFN1YmplY3QgPC0gYXMuZmFjdG9yKGRmQWN0aXZpdHkkU3ViamVjdCkKcm93bmFtZXMoZGZBY3Rpdml0eSkgPC0gTlVMTApkZkFjdGl2aXR5ICU+JSBzZWxlY3QoYygiU3ViamVjdCIsICJBY3Rpdml0eU5hbWUiKSkKYGBgCgpgYGB7cn0KbGlicmFyeShkZW5kZXh0ZW5kKQoKTlVNQkVSX09GX0NMVVNURVJTID0gMwoKY29sb3JfZGFya3BpbmsgPSAiI2U3NTQ4MCIKQ0xVU1RFUl9CUkFOQ0hfQ09MT1JTIDwtIGMoImJsdWUiLCAicmVkIiwgY29sb3JfZGFya3BpbmspWzE6TlVNQkVSX09GX0NMVVNURVJTXQpDTFVTVEVSX0xBQkVMX0NPTE9SUyA8LSBjKCJibHVlIiwgInJlZCIsIGNvbG9yX2RhcmtwaW5rKVsxOk5VTUJFUl9PRl9DTFVTVEVSU10KCmJlaGF2aW9yYWxNYXRyaXhDbHVzdGVyaW5nIDwtIGFzLm1hdHJpeChjbHVzdGVyaW5nRGYpCnJvd25hbWVzKGJlaGF2aW9yYWxNYXRyaXhDbHVzdGVyaW5nKSA8LSBwYXN0ZTAoZGZBY3Rpdml0eSRBY3Rpdml0eU5hbWUsICIgLSAjIiwgcGVyc29ucykKZGlzdE1hdHJpeCA8LSBkaXN0KGJlaGF2aW9yYWxNYXRyaXhDbHVzdGVyaW5nLCBtZXRob2Q9Im1hbmhhdHRhbiIpCmhyZXN1bHRzIDwtIGRpc3RNYXRyaXggJT4lIGhjbHVzdAoKaGMgPC0gaHJlc3VsdHMgJT4lIAogICAgICBhcy5kZW5kcm9ncmFtICU+JQogICAgICBzZXQoIm5vZGVzX2NleCIsIE5VTUJFUl9PRl9DTFVTVEVSUykgJT4lCiAgICAgIHNldCgibGFiZWxzX2NvbCIsIHZhbHVlID0gQ0xVU1RFUl9MQUJFTF9DT0xPUlMsIGs9TlVNQkVSX09GX0NMVVNURVJTKSAlPiUKICAgICAgIyBzZXQoImxlYXZlc19wY2giLCAxOSkgJT4lCiAgICAgICMgc2V0KCJsZWF2ZXNfY29sIiwgdmFsdWUgPSBjKCJncmF5IiksIGs9TlVNQkVSX09GX0NMVVNURVJTKSAlPiUgICAgCiAgICAgIHNldCgiYnJhbmNoZXNfa19jb2xvciIsIHZhbHVlPUNMVVNURVJfQlJBTkNIX0NPTE9SUywgaz1OVU1CRVJfT0ZfQ0xVU1RFUlMpCgpwbG90KGhjKQpsZWdlbmQoInRvcHJpZ2h0IiwgCiAgICAgdGl0bGU9IkRyaXZlPUZhaWx1cmUgXG5DaGFuZ2Ugb2YgQXJvdXNhbCIsCiAgICAgbGVnZW5kID0gYygiRXhjZXB0aW9uYWwgSW5jcmVhc2UiICwgIk5vdGljYWJsZSBJbmNyZWFzZSIgLCAiTm8tY2hhbmdlIG9yIERlY3JlYXNlIiksIAogICAgIGNvbCA9IGMoInJlZCIsICJwaW5rIiAsICJibHVlIiksCiAgICAgcGNoID0gYygyMCwyMCwyMCksIGJ0eSA9ICJuIiwgIHB0LmNleCA9IDEuNSwgY2V4ID0gMC44ICwgCiAgICAgdGV4dC5jb2wgPSAiYmxhY2siLCBob3JpeiA9IEZBTFNFLCBpbnNldCA9IGMoMC4wLCAwLjEpKQpgYGAKCmBgYHtyfQpOVU1CRVJfT0ZfQ0xVU1RFUlMgPSAyCgpjb2xvcl9kYXJrcGluayA9ICIjZTc1NDgwIgpDTFVTVEVSX0JSQU5DSF9DT0xPUlMgPC0gYygiYmx1ZSIsICJyZWQiLCBjb2xvcl9kYXJrcGluaylbMTpOVU1CRVJfT0ZfQ0xVU1RFUlNdCkNMVVNURVJfTEFCRUxfQ09MT1JTIDwtIGMoImJsdWUiLCAicmVkIiwgY29sb3JfZGFya3BpbmspWzE6TlVNQkVSX09GX0NMVVNURVJTXQoKYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcgPC0gYXMubWF0cml4KGNsdXN0ZXJpbmdEZikKcm93bmFtZXMoYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcpIDwtIHBhc3RlMChkZkFjdGl2aXR5JEFjdGl2aXR5TmFtZSwgIiAtICMiLCBwZXJzb25zKQpkaXN0TWF0cml4IDwtIGRpc3QoYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcsIG1ldGhvZD0ibWFuaGF0dGFuIiwgZGlhZyA9IFQpCmhyZXN1bHRzIDwtIGRpc3RNYXRyaXggJT4lIGhjbHVzdChtZXRob2Q9ImF2ZXJhZ2UiKQoKaGMgPC0gaHJlc3VsdHMgJT4lIAogICAgICBhcy5kZW5kcm9ncmFtICU+JQogICAgICBzZXQoIm5vZGVzX2NleCIsIE5VTUJFUl9PRl9DTFVTVEVSUykgJT4lCiAgICAgIHNldCgibGFiZWxzX2NvbCIsIHZhbHVlID0gQ0xVU1RFUl9MQUJFTF9DT0xPUlMsIGs9TlVNQkVSX09GX0NMVVNURVJTKSAlPiUKICAgICAgIyBzZXQoImxlYXZlc19wY2giLCAxOSkgJT4lCiAgICAgICMgc2V0KCJsZWF2ZXNfY29sIiwgdmFsdWUgPSBjKCJncmF5IiksIGs9TlVNQkVSX09GX0NMVVNURVJTKSAlPiUgICAgCiAgICAgIHNldCgiYnJhbmNoZXNfa19jb2xvciIsIHZhbHVlPUNMVVNURVJfQlJBTkNIX0NPTE9SUywgaz1OVU1CRVJfT0ZfQ0xVU1RFUlMpCgpwbG90KGhjKQpsZWdlbmQoInRvcHJpZ2h0IiwgCiAgICAgdGl0bGU9IkRyaXZlPUZhaWx1cmUgXG5DaGFuZ2Ugb2YgQXJvdXNhbCIsCiAgICAgbGVnZW5kID0gYygiQWNjZWxlcm9waG9iaWEiICwgIk5vcm1hbCIpLCAKICAgICBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIpLAogICAgIHBjaCA9IGMoMjAsMjAsMjApLCBidHkgPSAibiIsICBwdC5jZXggPSAxLjUsIGNleCA9IDAuOCAsIAogICAgIHRleHQuY29sID0gImJsYWNrIiwgaG9yaXogPSBGQUxTRSwgaW5zZXQgPSBjKDAuMCwgMC4xKSkKYGBgCgpgYGB7cn0KIyBTdG9yZSBjbHVzdGVyaW5nIGRhdGEKZGZ4IDwtIGNsdXN0ZXJpbmdEZgpkZnggPC0gY2JpbmQocGVyc29ucywgYXMubnVtZXJpYyhiZWhhdmlvcmFsRGYkUFBfYmVmb3JlKSwgYXMubnVtZXJpYyhiZWhhdmlvcmFsRGYkUFBfdSksIGRmeCwgZGZBY3Rpdml0eSRBY3Rpdml0eU5hbWUpCm5hbWVzKGRmeCkgPC0gYygiU3ViamVjdCIsICJQUF9QcmlvciIsICJQUF9BZnRlciIsICJQUF9EZXYiLCAiQWN0aXZpdHkiKQoKIyBOb3JtYWxpemUKZGZ4IDwtIGRmeCAlPiUgbXV0YXRlKFBQX0Rldl9Ob3JtYWxpemVkID0gaWZlbHNlKFBQX1ByaW9yID4gMCwgUFBfQWZ0ZXIsIFBQX0FmdGVyIC0gUFBfUHJpb3IpKQp3cml0ZS5jc3YoZGZ4LCBvdXRwdXRGaWxlLCByb3cubmFtZXMgPSBGKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXIpCmZpdCA8LSBrbWVhbnMoY2x1c3RlcmluZ0RmLCAyKQpjbHVzcGxvdChjbHVzdGVyaW5nRGYsIGZpdCRjbHVzdGVyLCBjb2xvcj1UUlVFLCBzaGFkZT1UUlVFLAogICBsYWJlbHM9MiwgbGluZXM9MCkKYGBgCgpgYGB7cn0Kc2lsaG91ZXR0ZV9zY29yZSA8LSBmdW5jdGlvbihrKXsKICBrbSA8LSBrbWVhbnMoY2x1c3RlcmluZ0RmLCBjZW50ZXJzID0gaywgbnN0YXJ0PTI1KQogIHNzIDwtIHNpbGhvdWV0dGUoa20kY2x1c3RlciwgZGlzdChjbHVzdGVyaW5nRGYpKQogIG1lYW4oc3NbLCAzXSkKfQprIDwtIDI6MTAKYXZnX3NpbCA8LSBzYXBwbHkoaywgc2lsaG91ZXR0ZV9zY29yZSkKcGxvdChrLCB0eXBlPSdiJywgYXZnX3NpbCwgeGxhYj0nTnVtYmVyIG9mIGNsdXN0ZXJzJywgeWxhYj0nQXZlcmFnZSBTaWxob3VldHRlIFNjb3JlcycsIGZyYW1lPUZBTFNFKQpgYGAKCiMjIExpbmVhciBNb2RlbApgYGB7cn0Kc2FtcGxlZERhdGEgPC0gZ2V0U2FtcGxlU2VnbWVudGVkRGF0YShOQSwgYWxsX0RyaXZlNCwgd2luZG93PTIpCmxpbmVhck1vZGVsT25saW5lIDwtIGxtZXIocHBOZXh0IH4gCiAgICAgICAgICAgICAgKDEgfCBTdWJqZWN0KQogICAgICAgICAgICAgICsgU3BlZWRfdQogICAgICAgICAgICAgICsgU3BlZWRfc3RkCiAgICAgICAgICAgICAgKyBBY2NfdQogICAgICAgICAgICAgICsgQWNjX3N0ZAogICAgICAgICAgICAgICsgQnJha2VfdQogICAgICAgICAgICAgICsgQnJha2Vfc3RkCiAgICAgICAgICAgICAgKyBTdGVlcmluZ191CiAgICAgICAgICAgICAgKyBTdGVlcmluZ19zdGQsIAogICAgICAgICAgICBkYXRhPXNhbXBsZWREYXRhLCBSRU1MID0gVCkKCiMgbG1lcihwcExvZ05vcm1hbGl6ZWQgfiAoMSB8IFN1YmplY3QpICsgU3BlZWRfdSArIFNwZWVkX3N0ZCArIEFjY191ICsgQWNjX3N0ZCArIEJyYWtlX3UgKyBCcmFrZV9zdGQgKyBTdGVlcmluZ191ICsgU3RlZXJpbmdfc3RkICsgSFIgKyBCUiwgZGF0YSA9IHBEYXRhLCBSRU1MID0gVCkKCiMgYW5vdmEobW9kZWwpCnN1bW1hcnkobGluZWFyTW9kZWxPbmxpbmUpCnBsb3QobGluZWFyTW9kZWxPbmxpbmUpCmBgYAo=